import re, os
from natsort import natsorted
from colorama import Fore, init
init(autoreset=True)

from multiprocessing import Pool # JoinableQueue, Process, Queue 
import multiprocessing
import threading
from io import StringIO
import traceback

from hm.hos import *
from hm.multi_process import MyPool
from report import to_pdf, log, logger
from ODBC_report import printf, FIX, Serializer
from execu_summary import Execution, Summary
from index_DTR import Index, DTR, SumOfChange
from final_report import FinalReport
from ODBC_report import save_sign_page
from report import auto_number_to_text_many, WordApp


def find_execution_dir(folder):
    printf(f"{Fore.LIGHTBLACK_EX}😶正在查找execution文件夹.", end='')
    for cur, dirs, files in os.walk(folder):
        printf(f"{Fore.LIGHTBLACK_EX}.", end='')
        for dir_ in dirs:
            if re.search(r"^[^\.].*Test Execut", dir_): 
                printf(f"{Fore.LIGHTBLACK_EX}Execution dir:{os.path.join(cur, dir_)}")
                return os.path.join(cur, dir_)
        for file in [i for i in files if i.endswith(".docx")]:
            try:
                e = Execution(OS.join(cur, file))
                if e.is_me:
                    printf(f"{Fore.LIGHTBLACK_EX}Execution dir:{cur}")
                    return cur
            except:
                continue
    printf(f'{Fore.RED}此目录{folder}下找不到执行文件夹')
    sys.exit(0)



def get_exec_paths(folder):
    exec_dir = find_execution_dir(folder)
    ret = [os.path.join(exec_dir, file) for file in natsorted(os.listdir(exec_dir)) 
            if re.search(r"\.doc.?$", file) and not OSis_hidden(file)]
    auto_number_to_text_many(ret)
    return ret


def get_report_type(path):
    types = [Execution, Summary, DTR, Index]  #, SumOfChange, FinalReport]
    for report_type in types:
        try:
            ins = report_type(path)
            if ins.is_me: 
                return ins
        except:
            logger.warning(f"{path} is not {report_type.__name__}, {traceback.format_exc()}")
        


def check_ins(ins):
    '''
        【公用的】检查报告对象
    '''
    try:
        # 仅打印pdf
        if '--pdf' in sys.argv:
            pdf_path = to_pdf(ins.path)
            save_sign_page(pdf_path)
            if isinstance(ins, Execution) and ins.summary_path:
                pdf_path = to_pdf(ins.summary_path)
                save_sign_page(pdf_path)
        # 仅获取页码
        elif '--page' in sys.argv and not isinstance(ins, Index):
            printf(f'找到文件{ins.filename}')
            if isinstance(ins, Execution) and ins.summary_path:
                ins.s = Summary(ins.summary_path)
        else:
            for err in ins.check():
                printf(err)
            ins.save()
    except:
        err = f"检查内容错误： {ins.path}"
        logger.error(err+f", {traceback.format_exc()}")
        printf(err)
    return ins


def check_file(path, report_type, *args):
    '''
        【公用的】检查报告文件
    '''
    try:
        err = ''
        ins = report_type(path, *args)
        if ins.is_me: 
            return check_ins(ins)
        else:
            err = f"is not a {report_type.__name__}"
    except Exception as e:
        err = f"is wrong file {str(e)}"
        logger.error("检查文件错误"+ traceback.format_exc())
    printf(f"{Fore.RED}Error!{path} {err}")
    

def check_execution(path):
    '''
        检查execution, 同时检查summary
    '''
    e = check_file(path, Execution)
    if e:
        try:
            if not e.summary_path:
                printf(f"    {Fore.RED}找不到对应的的TestSummaryReport，检查文件后缀名是否一致")
                if FIX:
                    printf(f"    尝试创建Sumary文件")
                    e.s = Summary.create_summary(e)
                    e.summary_path =  e.s.path
                    printf(f"    {Fore.GREEN}创建成功，路径：{e.summary_path}")
            else:
                e.s = check_summary(e)
        except Exception as e:
            printf(f"    {Fore.RED}没找到对应的Summary: {str(e)}")
            logger.info(traceback.format_exc())
        return e
 
    
def check_summary(e):
    return check_file(e.summary_path, Summary, e)


# +++++++++++++ 单进程获取executions ++++++++++++++++
def get_execus(paths):
    executions = []
    for path in paths:
        e = check_execution(path)
        if e: 
            executions.append(e) 
    return executions

# +++++++++++++ 多进程获取executions ++++++++++++++++
def check_execution_p(paths, log_q):
    multiprocessing.current_process().name = paths[0] # for order the log
    log.switch_to_queue(log_q)
    ret = []
    for path in paths:
        e = check_execution(path)
        if e:
            ret.append(e.serializer)
    return ret

def get_log(log_q, event):
    log_records = {}
    while not event.is_set():
        if not log_q.empty():
            log_record = log_q.get()
            if log_record.processName not in log_records:
                log_records[log_record.processName] = []
            log_records[log_record.processName].append(log_record)
    # 写日志
    for name in natsorted([i for i in log_records]):
        for log_record in log_records[name]:
            log.fh.emit(log_record)


def get_execus_pool(paths):    
    pool = MyPool()
    pool.print = printf
    log_q = multiprocessing.Manager().Queue() # 用来收集子进程log信息
    executions = []
    try:
        start, end = 0, 0
        while end<len(paths):
            end += 10
            if end>len(paths):
                end=len(paths)
            pool.apply_async(check_execution_p, paths[start:end], log_q)
            start = end
        # collect son process log
        event = threading.Event()
        t = threading.Thread(target=get_log, args=(log_q, event,))
        t.setDaemon(True)
        t.start()
        executions, errors = pool.read()
        event.set()
        t.join()
        for err in errors:
            logger.error(err)
    except KeyboardInterrupt:
        print("Interrupted by user, trying to terminate pool")
        logger.error("KeyboardInterrupt")
        pool.terminate()  
        sys.exit(0)
    return [j for i in executions for j in i]
    
    
    
def check_index_other(path):
    # 检查index, DTR, summary of change, 总报告
    if '--pdf' in sys.argv:
        executions = get_execus(path) # pdf多进程容易报错...
    else:
        paths = get_exec_paths(path)
        if len(paths)>0:
            executions = get_execus_pool(paths)
        else:
            executions = get_execus(paths)
        print(f"😶 总共{len(paths)}个execution文件，{len(executions)}个已处理")
    
    if executions:
        e1 = executions[0]
        d = [(e1.DTR_path, DTR),  
             (e1.summary_changes_path, SumOfChange), 
             (e1.Index_path, Index), # 因为它要统计上面两个页码，所以放最后
             (e1.final_report_path, FinalReport), 
        ]
        for i in d:
            file_path, report_type = i[0], i[1]
            if not file_path:
                printf(f"{Fore.YELLOW}找不到{report_type.__name__}文件")
                # if FIX:
                printf("尝试创建...")
                res = f"{Fore.GREEN}创建成功" if report_type.create_me(executions) else f"{Fore.RED}创建失败"
                printf(res)
            else:
                check_file(file_path, report_type, executions)
            if report_type==FinalReport:
                report_type.create_tables(executions)
    else:
        printf(f"{Fore.YELLOW}找不到execution文件")
 

def check(path):
    # todo 1) auto number 2)pages slowly  3) 脚本会关闭当前word打开的文档
    try:
        if os.path.isfile(path):
            # 如果文件不是任何支持的报告类型（如格式不对 ），阻止进到后面的的代码
            ins = get_report_type(path)
            print("文件类型", ins)
            if not ins:
                return printf(f"{Fore.RED}不支持的类型，请检查文件格式/内容")
            if isinstance(ins, Summary):
                if not ins.execution_path:
                    return printf(f"    {Fore.RED}未找到对应execution报告或程序错误")
                e = check_execution(ins.execution_path)
                return check_summary(e)
            elif isinstance(ins, Execution):
                return check_execution(path)
            else:
                path = os.path.dirname(path)
        check_index_other(path)  
    except Exception as e:
        print("程序错误", str(e))
        logger.error("程序错误"+traceback.format_exc())

     